﻿$Global:DeploymentUserCredential
Function Execute-DeploymentPhase
{

    [cmdletbinding(SupportsShouldProcess=$True)]
    Param( 
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String]
    $ComponentName,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String]
    $DeploymentStep,

    [Parameter(Mandatory=$false,ValueFromPipeline=$true)] 
    [Switch]
    $Asynchronous
    )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Execute-DeploymentPhase for $DeploymentStep"
        $DeploymentStep = $DeploymentStep.ToLower()
    }
    End
    {
        write-verbose "End Execute-DeploymentPhase for $DeploymentStep"
        $watch.Stop();
        Write-Verbose "Execute-DeploymentPhase duration : $($watch.Elapsed)"
    }
    Process 
    {

        #Storage for Asynchronous jobs
        $asynchronousJobs = @()

		
		$whatifActivated = ($PSBoundParameters['WhatIf'] -eq $True)
		$verboseActivated = ($PSBoundParameters['Verbose'] -eq $True)
		
		
        #Browse all Loaded servers
        foreach($server in $Servers)
        {
            Execute-DeploymentPhaseForServer -Server $server -ComponentName $ComponentName -DeploymentStep $DeploymentStep -WhatIf:$whatifActivated  -Verbose:$verboseActivated
        }

        #Browse Asynchronous jobs to get the result
        if($Asynchronous)
        {
            $asynchronousJobs |Wait-Job
            $asynchronousJobs | Receive-Job
        }
             
    }
}

Function Execute-DeploymentPhaseForServer
{

    [cmdletbinding(SupportsShouldProcess=$True)]
    Param( 
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [System.Object]
    $Server,
    
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String]
    $ComponentName,
    
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String]
    $DeploymentStep,    

    [Parameter(Mandatory=$false,ValueFromPipeline=$true)] 
    [Switch]
    $Asynchronous
    )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Execute-DeploymentPhaseForServer for $($Server.Name)"
        $DeploymentStep = $DeploymentStep.ToLower()
    }
    End
    {
        write-verbose "End Execute-DeploymentPhaseForServer for $($Server.Name)"
        $watch.Stop();
        Write-Verbose "Execute-DeploymentPhaseForServer duration : $($watch.Elapsed)"
    }
    Process 
    {

		
		$whatifActivated = ($PSBoundParameters['WhatIf'] -eq $True)
		$verboseActivated = ($PSBoundParameters['Verbose'] -eq $True)
		
        #Get component's Actions
        $ComponentActions = Get-ComponentActions -ComponentName $ComponentName -DeploymentStep $DeploymentStep

		
        $component = $Server.Components | Where-Object {$_.ComponentName -eq $ComponentName}
        if ($component -ne $null)
        {
                
            #browse components to deploy
            foreach($componentToDeploy in $component)
            {
                    
                write-verbose "Find $($componentToDeploy.ComponentName) with name $($componentToDeploy.Name) for server $($Server.Name)"
                Write-Verbose "Check if deploymentStep '$DeploymentStep' exist for this component in '$($componentToDeploy.DeploymentSteps)'"

                <#
                if (($componentToDeploy.DeploymentSteps.Split(";").IndexOf($DeploymentStep)) -lt 0)
                {
                    Write-Warning "The component $($componentToDeploy.ComponentName) with name $($componentToDeploy.Name) is not configured for the deployment step '$DeploymentStep' on Server ' $($server.Name)'"
                    continue 
                }
                #>


                $commandToExecute = ([scriptblock]::create("Execute-Actions -Server $Server -Component $component -ComponentActions $ComponentActions -DeploymentStep $DeploymentStep"))
                    
                #Choose local execution or remote connection
                $runtimeServer = Get-Item env:computername
                    
                if (($Server.HostName -eq 'localhost') -or ($Server.HostName -eq $runtimeServer))
                {
                    write-verbose "Local execution on $($Server.Name)"
                    $resultExecuteAction = Invoke-Command -ScriptBlock {param($v1,$v2,$v3,$v4,$v5,$v6) Execute-Actions -Server $v1 -Component $v2 -ComponentAction $v3 -DeploymentStep $v4 -WhatIf:$v5 -Verbose:$v6} -ArgumentList $Server, $componentToDeploy, $ComponentActions, $DeploymentStep, $whatifActivated, $verboseActivated
                    
                }
                else
                {
                    write-verbose "Remote execution on $($Server.HostName)"
                    $serverSession = Get-RemoteSession -Server $Server
                    $resultExecuteAction = Invoke-Command -Session $serverSession -ScriptBlock {param($v1,$v2,$v3,$v4,$v5,$v6) Execute-Actions -Server $v1 -Component $v2 -ComponentAction $v3 -DeploymentStep $v4 -WhatIf:$v5 -Verbose:$v6} -ArgumentList $Server, $componentToDeploy, $ComponentActions, $DeploymentStep, $whatifActivated, $verboseActivated
                    
                    
                }
                    
                Out-ToLogXmlFormat -Path $Configuration.Logger.Logfile -Server $Server -ComponentName $componentToDeploy.Name -InputObject $resultExecuteAction
                Write-Output $resultExecuteAction
                    
                    <#                    
                    if ($Asynchronous)
                    {
                        $initScript = "Import-Module 'D:\Devel\PG Installer\Application.Management\Application.Management.psd1';"
                        $initScript += "Initialize-Environment -DeploymentTarget 'D:\Devel\PG Installer\deploymentTarget.xml' -ActionsList 'D:\Devel\PG Installer\DeployActions.xml'"


                        $job = Start-Job -ScriptBlock $commandToExecute -Name "ExecAct_Serv$($server.Name)_Comp$($component.Name)" -InitializationScript ([scriptblock]::create($initScript))
                        $asynchronousJobs += $job
                    }
                    else
                    {
                        #Invoke-Command -ScriptBlock {Execute-Actions -Server $server -Component $component -ComponentActions $ComponentActions -DeploymentStep $DeploymentStep}
                        #Invoke-Command -ScriptBlock $commandToExecute
                        Invoke-Command -ScriptBlock {param($v1,$v2,$v3,$v4) Execute-Actions -Server $v1 -Component $v2 -ComponentAction $v3 -DeploymentStep $v4} -ArgumentList $server, $component, $ComponentActions, $DeploymentStep

                    }
                    #>
            }#End browsing components
        }
        else
        {
            Write-Verbose "Server $($Server.Name) doesn't have this component $($ComponentName)"
        }
        
        Write-Verbose "End Execute-Deployment phase for $($Server.Name)"
             
    }
}




Function Execute-Actions
{
    [cmdletbinding(SupportsShouldProcess=$True)]
    Param( 
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [System.Object]
    $Server,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [System.Object]
    $Component,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [System.Object]
    $ComponentAction,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String]
    $DeploymentStep
    )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Execute-Actions for $DeploymentStep $($component.Name) on $($Server.Name)"
		$whatifActivated = ($PSBoundParameters['WhatIf'] -eq $True)
		$verboseActivated = ($PSBoundParameters['Verbose'] -eq $True)
		write-verbose "Mode verbose = $verboseActivated  and Mode Whatif = $whatifActivated"
    }
    End
    {
        write-verbose "End Execute-Actions for $DeploymentStep $($component.Name) on $($Server.Name)"
        $watch.Stop();
        Write-Verbose "Execute-Actions duration : $($watch.Elapsed)"
    }
    Process 
    {
        try
        {
        
            #Write-Verbose "browse $($ComponentActions.Count) actions"
            #foreach($ComponentAction in $ComponentActions)
            #{

                $componentStatus = New-Object PSObject -Property @{
                                    ServerName=$($Server.Name)
                                    ComponentName=$($Component.Name)
                                    DeploymentStep=$DeploymentStep
                                    Status=$false
                                    Results =@()
                                    Start=$(Get-date)
                                    End=$null
                                    Duration=$null
                                    }


                Write-Verbose "This componnent $($Component.Name) has $($ComponentAction.Actions.Count) actions"
                
                #Get Detailed actions to execute
                $ComponentAction.Actions | Sort-Object -Property Sequence | ForEach-Object {

                    
                    #Execute Command
                    $resultAction =  New-Object PSObject -Property @{Command="Create-Command"
                                                                        Status=$false
                                                                        Result=""
                                                                        ExitCode = -1
                                                                        Exception=""
                                                                        Warning = ""
                                                                        Start=$(Get-date)
                                                                        End=$null
                                                                        Duration=$null
                                                                        }
                                                                        
                    try
                    {
                        $commandToExecute = Create-Command -Server $Server -Component $Component -Action $_ -WhatIf:$PSBoundParameters['WhatIf'] -Verbose:$PSBoundParameters['Verbose']
                        $resultAction.Command = $commandToExecute
                        Write-Verbose "Command To Execute is : $commandToExecute"

                        try
                        {
							$LASTEXITCODE = 0
                            $resultAction.Result = Invoke-Command -ScriptBlock ([scriptblock]::create($commandToExecute)) -ErrorVariable errorAction -ErrorAction Continue -WarningVariable warningAction
                            $resultAction.ExitCode = $LASTEXITCODE
                        }
                        catch [Exception]
                        {
                            $resultAction.ExitCode = -1
                            $errorAction = $_.Exception.Message
                            
                        }
                        
                        if ($errorAction -ne $null)
                        {
                            Write-Verbose "Detected Error in ErrorAction variable : $errorAction"
                            $resultAction.Exception = $errorAction
                            if (($resultAction.ExitCode -eq $null) -or ($resultAction.ExitCode -eq 0)) {$resultAction.ExitCode=-1 }
                        }
                        
                        if ($warningAction -ne $null)
                        {
                            Write-Verbose "Detected Warning in WarningAction variable : $warningAction"
                            $resultAction.Warning = $warningAction                            
                        }

                        if ($resultAction.ExitCode -eq $null) {$resultAction.ExitCode=0 }
                        
                        
                        $resultAction.Status = ($resultAction.ExitCode -eq 0) -or ($resultAction.ExitCode -eq $null)
                        Write-Verbose "Command Execution is finised and status is retrieved : $($resultAction.Status)"
                    }
                    catch [Exception]
                    {
                        $resultAction.Exception = $_.Exception
                        Write-Warning "Command Execution has unexpected exception : $($_.Exception)"
                    }

                    $resultAction.End = $(get-Date)
                    $resultAction.Duration = $(($resultAction.End - $resultAction.Start).ToString())
                    $componentStatus.Results += $resultAction  
                
                }#end browsing Actions

                #return the component status deployment
                $componentStatus.Status = @($componentStatus.Results | Where-Object {$_.Status -eq $false}).Count -eq 0
                $componentStatus.End = $(get-Date)
                $componentStatus.Duration = $(($componentStatus.End - $componentStatus.Start).ToString())

                Write-Output $componentStatus

            #}#end browing component

        }
        catch [Exception]
        {
            write-Error "Error Execute-Actions : $($_.Exception.Message)"
        }                
    }

}


Function Create-Command
{
    [cmdletbinding(SupportsShouldProcess=$True)]
    Param( 
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [System.Object] 
    $Server,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [System.Object] 
    $Component,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [System.Object] 
    $Action

    )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Create-Command from $($Action.CommandToExecute) on $($Server.Name)"
    }
    End
    {
        write-verbose "End Create-Command from $($Action.CommandToExecute) on $($Server.Name)"
        $watch.Stop();
        Write-Verbose "Create-Command duration : $($watch.Elapsed)"
    }
    Process 
    {
    
        #Retrieve the command to execute. Apply lookup value if dynamic lookup pattern exist in the command
        $commandToExecute = Convert-PatternToValue_v2 -Server $Server -Component $Component -Pattern $Action.CommandToExecute

        foreach($argument in $Action.Arguments)
        {
            if ($argument.value)
            {
                write-verbose "Found argument value : '$($argument.value)'. Call Convert-PatternToValue"
                $argumentValue = Convert-PatternToValue_v2 -Server $Server -Component $Component -Pattern $argument.value
                
                if (![System.String]::IsNullOrEmpty($argumentValue))
                {
                    $commandToExecute = [System.String]::Concat($commandToExecute, ' ', $argument.name, ' ', $argumentValue)
                }    
            }
            else
            {
                $commandToExecute = [System.String]::Concat($commandToExecute, ' ', $argument.name)
            }
            
        }

        
        #Add Verbose support
        if ($Action.SupportVerbose -and $PSBoundParameters['Verbose'])
        {
            Write-Verbose "Add Verbose to the command : Command support Verbose is equal to '$($Action.SupportVerbose)' and switch Verbose has value '$($PSBoundParameters['Verbose'])'"
            $commandToExecute += " -Verbose"
        }

        #Add whatif support
        if ($PSBoundParameters['Whatif'])
        {
            if($Action.SupportWhatif)
            {
                Write-Verbose "Add Whatif to the command : Command support Whatif is equal to '$($Action.SupportWhatif)' and switch whatif has value '$($PSBoundParameters['Whatif'])'"
                $commandToExecute += " -Whatif"
            }
            else
            {
                Write-Verbose "Create fake command  to have an echo for this command which doesn't support Whatif : '$($Action.SupportWhatif)'"
                $commandToExecute = "write-output 'WHATIF for unsupported whatif command : " + $commandToExecute + "'" 
                
            }
        }


        #write the command to execute
        Write-Output $commandToExecute


    }

}


Function Convert-PatternToValue
{
   Param( 
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [System.Object] 
    $Server,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [System.Object] 
    $Component,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String] 
    $Pattern
    )
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Convert-PatternToValue from $Pattern on Server $($Server.Name) and component $($Component.Name)"
    }
    End
    {
        write-verbose "End Convert-PatternToValue from $Pattern on Server $($Server.Name) and component $($Component.Name)"
        $watch.Stop();
        Write-Verbose "Convert-PatternToValue duration : $($watch.Elapsed)"
    }
    Process 
    {
        $argumentValue = $Pattern
        
        #Search in the value token with {{*}}
        #TODO : Enhance the regex to extract only the values. This regex retrieve 2 values per token                
        $regeExresult = [regex]::matches($argumentValue,'\{{([^\}]+)\}}')
                
                
        if ($regeExresult.Success)
        {
            
            #TODO : Enhance the regex result. We could find more one token to replace                
            $argumentPatternProperty = $regeExresult.Groups[1].Value
                    
                   
            if ($argumentPatternProperty.StartsWith("../@"))
            {
                write-verbose "Extract pattern  : ****$argumentPatternProperty***** and try to replace with value from $($Server.Name)"
                $searchProperty = $argumentPatternProperty.Replace("../@","")
                Write-Verbose "Retrieve the value $argumentPatternProperty on the server $($Server.Name) properties"
                $argumentValue = $Pattern.replace($argumentPatternProperty, $($Server.$searchProperty)).replace("{{","").replace("}}","")
            }
            else 
            {
                if ($argumentPatternProperty.StartsWith("@"))
                {
                    write-verbose "Extract pattern  : ****$argumentPatternProperty***** and try to replace with value from $($Component.Name)"
                    $searchProperty = $argumentPatternProperty.Replace("@","")
                    
                    $checkProperty = $component|Get-Member -Name $searchProperty
                    if ($checkProperty -ne $null)
                    {
                        Write-Verbose "Retrieve the value $argumentPatternProperty on the component $($Component.Name) properties"
                        $argumentValue = $Pattern.replace($argumentPatternProperty, $($Component.$searchProperty)).replace("{{","").replace("}}","")
                    }    
                }
                else
                {
                    throw New-Object System.Exception "Extract Pattern '$argumentPatternProperty' can't be interpreted because it doesn't respect the grammar : Must StartsWith (../@) or StartsWith(@)" 
                }
            }                    
                    
            
            #Add quote if the value contains spaces
            if ($argumentValue.Contains(" "))
            {
                $argumentValue = "'" + $argumentValue + "'"
            }
            write-verbose "Argument value after transformation : $argumentValue"
        }

        #write the result
        Write-Output $argumentValue

    }

}


Function Get-ComponentActions
{
    Param( 
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String]
    $ComponentName,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String]
    $DeploymentStep
    )
   
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Get-ComponentActions for $ComponentName and step $DeploymentStep"
    }
    End
    {
        write-verbose "End Get-ComponentActions for $ComponentName and step $DeploymentStep"
        $watch.Stop();
        Write-Verbose "Get-ComponentActions duration : $($watch.Elapsed)"
    }
    Process
    {
        if(($DeploymentActions -eq $null)  -or ($DeploymentActions.Count -eq 0))
        {
            Write-Warning "You should call Initialize-Environment before.... Deployment Actions list is empty"
            return
        }
        
        $actions = $DeploymentActions | Where-Object -FilterScript {($_.ComponentName -eq $ComponentName) -and ($_.Step -eq $DeploymentStep)}

        if (($actions -eq $null) -or ($actions.count -eq 0))
        {
            throw New-Object System.Exception -ArgumentList "No actions found for this component '$ComponentName'"
        }

        return $actions
    }
}

Function Get-ComponentActions_v2
{
    [cmdletbinding(DefaultParameterSetName="none")]
    Param( 
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String]
    $ComponentName,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true,ParameterSetName='ByType')] 
    [String]
    $ComponentType,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String]
    $DeploymentStep
    )
   
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Get-ComponentActions_v2 for $ComponentName and step $DeploymentStep"
    }
    End
    {
        write-verbose "End Get-ComponentActions for_v2 $ComponentName and step $DeploymentStep"
        $watch.Stop();
        Write-Verbose "Get-ComponentActions duration : $($watch.Elapsed)"
    }
    Process
    {
        #Check DeploymentActions list
        if(($DeploymentActions -eq $null)  -or ($DeploymentActions.Count -eq 0))
        {
            throw New-Object System.Exception -ArgumentList "You should call Initialize-Environment before.... Deployment Actions list is empty"
        }
        
        $actions = $DeploymentActions | Where-Object -FilterScript {($_.ComponentName -eq $ComponentName) -and ($_.Step -eq $DeploymentStep)}

        if (($actions -eq $null) -or ($actions.count -eq 0))
        {
            #We don't find actions for the component Name, if component Type exist we have to search for a generic actions            
            if ($ComponentType)
            {
                write-verbose "Don't find any actions for the component $ComponentName, search for the componentType $ComponentType and step $DeploymentStep"            
                $actions = $DeploymentActions | Where-Object -FilterScript {($_.componentType -eq $ComponentType) -and ($_.Step -eq $DeploymentStep)}
            }                    
        }

        if (($actions -eq $null) -or ($actions.count -eq 0))
        {
            #Error because no actions exist for the component
            throw New-Object System.Exception -ArgumentList "No actions found for this component '$ComponentName'"        
        }
        return $actions
    }
}



Function Test-Environment
{
   
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Test-Environment"
    }
    End
    {
        write-verbose "End Test-Environment"
        $watch.Stop();
        Write-Verbose "Function duration : $($watch.Elapsed)"
    }
    Process
    {
        if (($Servers -eq $null) -or ($Servers.Count -eq 0))
        {
            Write-Warning "NO Servers!!! You should call Initialize-Environment before...."
            return $false
        }

        if (($DeploymentActions -eq $null) -or ($DeploymentActions.Count -eq 0))
        {
            Write-Warning "NO Deployment Actions!!! You should call Initialize-Environment before...."
            return $false
        }


        #Browser server Configuration and check the infrastructure
        $checkConfiguration=$true
        foreach($server in $Servers)
        {

            #Remote session is available for server
            $sessionToken = Get-RemoteSession -Server $server -ErrorAction SilentlyContinue
            if ($sessionToken -eq $null)
            {
                Write-Warning "Unable to open a session on the server $($server.Name) !! You must verify the configuration or deploy the application management module"
                $checkConfiguration=$false
            }

            #Each component have deploymentActions
            foreach($component in $server.Components)
            {
                $Actions = $DeploymentActions |Where-Object {($_.ComponentName -eq $component.ComponentName) -or ($_.componentType -eq $component.ComponentType)}

                 if (($Actions -eq $null) -or ($Actions.Count -eq 0))
                 {
                     Write-Warning "No Actions for this component $($component.Name) on server definition $($server.Name) !! You must verify the Actions Lists...."
                     $checkConfiguration=$false
                 }

            }
        }

        return $checkConfiguration
        
    }
}

Function Get-ServerWithComponent
{
        Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
        [String] 
        $ComponentName                
        )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Get-ServerWithComponent for $ComponentName"
    }
    End
    {
        write-verbose "End Get-ServerWithComponent for $ComponentName"
        $watch.Stop();
        Write-Verbose "Function duration : $($watch.Elapsed)"
    }
    Process 
    {
        #Check that Servers exists
        if (($Servers -eq $null) -or ($Servers.Count -eq 0))
        {
            throw New-Object System.Exception "NO Servers!!! You should call Initialize-Environment before...."
        }
        

        $filteredServers = @()
        
        #Browse the server list
        $Servers | ForEach-Object {
        
            #test if component exists
            $searchComponent = $_.Components | Where-Object {$_.ComponentName -eq $ComponentName}
        
            if ($searchComponent -ne $null)
            {
                $filteredServers += $_
            }
        
        }
        
        
        
        
        
        Write-Output $filteredServers
        
        
    }
}

Function Execute-DeploymentPhase_v2
{

    [cmdletbinding(SupportsShouldProcess=$True)]
    Param( 
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String]
    $ComponentName,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String]
    $DeploymentStep,

    [Parameter(Mandatory=$false,ValueFromPipeline=$true)] 
    [Switch]
    $Asynchronous
    )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Execute-DeploymentPhase_v2 for $DeploymentStep"
        $DeploymentStep = $DeploymentStep.ToLower()
    }
    End
    {
        write-verbose "End Execute-DeploymentPhase_v2 for $DeploymentStep"
        $watch.Stop();
        Write-Verbose "Execute-DeploymentPhase duration : $($watch.Elapsed)"
    }
    Process 
    {

        #Storage for Asynchronous jobs
        $asynchronousJobs = @()

		
		$whatifActivated = ($PSBoundParameters['WhatIf'] -eq $True)
		$verboseActivated = ($PSBoundParameters['Verbose'] -eq $True)
		
        #Get servers with component
        $targetedServers = Get-ServerWithComponent -ComponentName $ComponentName
		if ($targetedServers -eq $null)
        {
            Write-Warning "NO Server found with this component $ComponentName"
            return
        }
        
        #Browse all Loaded servers
        foreach($server in $targetedServers)
        {
            Execute-DeploymentPhaseForServer_v2 -Server $server -ComponentName $ComponentName -DeploymentStep $DeploymentStep -WhatIf:$whatifActivated  -Verbose:$verboseActivated                
        }

        #Browse Asynchronous jobs to get the result
        if($Asynchronous)
        {
            $asynchronousJobs |Wait-Job
            $asynchronousJobs | Receive-Job
        }
             
    }
}

Function Execute-DeploymentPhaseForServer_v2
{

    [cmdletbinding(SupportsShouldProcess=$True)]
    Param( 
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [System.Object]
    $Server,
    
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String]
    $ComponentName,
    
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String]
    $DeploymentStep,    

    [Parameter(Mandatory=$false,ValueFromPipeline=$true)] 
    [Switch]
    $Asynchronous
    )

    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Execute-DeploymentPhaseForServer_v2 for $($Server.Name) and component $ComponentName for deployment step $DeploymentStep"
        $DeploymentStep = $DeploymentStep.ToLower()
    }
    End
    {
        write-verbose "End Execute-DeploymentPhaseForServer_v2 for $($Server.Name) and component $ComponentName for deployment step $DeploymentStep"
        $watch.Stop();
        Write-Verbose "Execute-DeploymentPhaseForServer_v2 duration : $($watch.Elapsed)"
    }
    Process 
    {

		
		$whatifActivated = ($PSBoundParameters['WhatIf'] -eq $True)
		$verboseActivated = ($PSBoundParameters['Verbose'] -eq $True)
				
        $component = $Server.Components | Where-Object {$_.ComponentName -eq $ComponentName}
        if ($component -ne $null)
        {
                
            #browse components to deploy
            foreach($componentToDeploy in $component)
            {
                    
                #Get component's Actions
                $ComponentActions = Get-ComponentActions_v2 -ComponentName $ComponentName -ComponentType $componentToDeploy.ComponentType  -DeploymentStep $DeploymentStep

                write-verbose "Find $($componentToDeploy.ComponentName) with name $($componentToDeploy.Name) for server $($Server.Name)"
                Write-Verbose "Check if deploymentStep '$DeploymentStep' exist for this component in '$($componentToDeploy.DeploymentSteps)'"

                <#
                if (($componentToDeploy.DeploymentSteps.Split(";").IndexOf($DeploymentStep)) -lt 0)
                {
                    Write-Warning "The component $($componentToDeploy.ComponentName) with name $($componentToDeploy.Name) is not configured for the deployment step '$DeploymentStep' on Server ' $($server.Name)'"
                    continue 
                }
                #>


                #$commandToExecute = ([scriptblock]::create("Execute-Actions -Server $Server -Component $component -ComponentActions $ComponentActions -DeploymentStep $DeploymentStep"))
                    
                #Choose local execution or remote connection
                $runtimeServer = Get-Item env:computername
                    
                if (($Server.HostName -eq 'localhost') -or ($Server.HostName -eq $runtimeServer))
                {
                    write-verbose "Local execution on $($Server.Name)"
                    $resultExecuteAction = Invoke-Command -ScriptBlock {param($v1,$v2,$v3,$v4,$v5,$v6) Execute-Actions -Server $v1 -Component $v2 -ComponentAction $v3 -DeploymentStep $v4 -WhatIf:$v5 -Verbose:$v6} -ArgumentList $Server, $componentToDeploy, $ComponentActions, $DeploymentStep, $whatifActivated, $verboseActivated
                    
                }
                else
                {
                    write-verbose "Remote execution on $($Server.HostName)"
                    $serverSession = Get-RemoteSession -Server $Server
                    $resultExecuteAction = Invoke-Command -Session $serverSession -ScriptBlock {param($v1,$v2,$v3,$v4,$v5,$v6) Execute-Actions -Server $v1 -Component $v2 -ComponentAction $v3 -DeploymentStep $v4 -WhatIf:$v5 -Verbose:$v6} -ArgumentList $Server, $componentToDeploy, $ComponentActions, $DeploymentStep, $whatifActivated, $verboseActivated
                                        
                }
                
                Out-ToLogXmlFormat -Path $Configuration.Logger.Logfile -Server $Server -ComponentName $componentToDeploy.Name -InputObject $resultExecuteAction
                Write-Output $resultExecuteAction
                    
                    <#                    
                    if ($Asynchronous)
                    {
                        $initScript = "Import-Module 'D:\Devel\PG Installer\Application.Management\Application.Management.psd1';"
                        $initScript += "Initialize-Environment -DeploymentTarget 'D:\Devel\PG Installer\deploymentTarget.xml' -ActionsList 'D:\Devel\PG Installer\DeployActions.xml'"


                        $job = Start-Job -ScriptBlock $commandToExecute -Name "ExecAct_Serv$($server.Name)_Comp$($component.Name)" -InitializationScript ([scriptblock]::create($initScript))
                        $asynchronousJobs += $job
                    }
                    else
                    {
                        #Invoke-Command -ScriptBlock {Execute-Actions -Server $server -Component $component -ComponentActions $ComponentActions -DeploymentStep $DeploymentStep}
                        #Invoke-Command -ScriptBlock $commandToExecute
                        Invoke-Command -ScriptBlock {param($v1,$v2,$v3,$v4) Execute-Actions -Server $v1 -Component $v2 -ComponentAction $v3 -DeploymentStep $v4} -ArgumentList $server, $component, $ComponentActions, $DeploymentStep

                    }
                    #>
            }#End browsing components
        }
        else
        {
            Write-Verbose "Server $($Server.Name) doesn't have this component $($ComponentName)"
        }
        
        Write-Verbose "End Execute-Deployment phase for $($Server.Name)"
             
    }
}

Function Convert-PatternToValue_v2
{
   Param( 
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [System.Object] 
    $Server,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [System.Object] 
    $Component,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [String] 
    $Pattern
    )
    Begin
    {
        $watch = [system.diagnostics.stopwatch]::StartNew() 
        write-verbose "Start Convert-PatternToValue from $Pattern on Server $($Server.Name) and component $($Component.Name)"
    }
    End
    {
        write-verbose "End Convert-PatternToValue from $Pattern on Server $($Server.Name) and component $($Component.Name)"
        $watch.Stop();
        Write-Verbose "Convert-PatternToValue duration : $($watch.Elapsed)"
    }
    Process 
    {
        $argumentValue = $Pattern
        
        #Search in the value token with {{*}}
        #TODO : Enhance the regex to extract only the values. This regex retrieve 2 values per token                
        $regeExresultats = [regex]::matches($argumentValue,'\{{([^\}]+)\}}')
        
        
        if ($regeExresultats)
        {
            $regeExresultats | ForEach-Object -Process {
            
                if ($_.Success)
                {                
                    #TODO : Enhance the regex result. We could find more one token to replace                
                    $argumentPatternProperty = $_.Groups[0].Value
                
                    if ($argumentPatternProperty.StartsWith("{{../@"))
                    {
                        write-verbose "Extract pattern  : ****$argumentPatternProperty***** and try to replace with value from $($Server.Name)"
                        $searchProperty = $argumentPatternProperty.Replace("../@","").Replace("{{","").Replace("}}","")
                        Write-Verbose "Retrieve the value $argumentPatternProperty on the server $($Server.Name) properties"
                        if (($Server.$searchProperty) -ne $null)
                        {
                            #throw New-Object System.Exception "Error null value on property $searchProperty for this server $($Server.Name)"
                            $argumentValue = $argumentValue.replace($argumentPatternProperty, $($Server.$searchProperty))
                        }
                        else
                        {
                            $argumentValue = [System.String]::Empty
                        }
                        
                    }
                    else 
                    {
                        if ($argumentPatternProperty.StartsWith("{{@"))
                        {
                            write-verbose "Extract pattern  : ****$argumentPatternProperty***** and try to replace with value from $($Component.Name)"
                            $searchProperty = $argumentPatternProperty.Replace("@","").Replace("{{","").Replace("}}","")
                            Write-Verbose "Retrieve the value $argumentPatternProperty on the component $($Component.Name) properties"
                            if (($Component.$searchProperty) -ne $null)
                            {
                                #throw New-Object System.Exception "Error null value on property $searchProperty for this component $($Component.Name)"
                                $argumentValue = $argumentValue.replace($argumentPatternProperty, $($Component.$searchProperty))
                            }
                            else
                            {
                                $argumentValue = [System.String]::Empty
                            }
                            
                        }
                        else
                        {
                            throw New-Object System.Exception "Extract Pattern '$argumentPatternProperty' can't be interpreted because it doesn't respect the grammar : Must StartsWith (../@) or StartsWith(@)" 
                        }
                    }                    
                
                }            
            }
        }
                    
       
        #Add quote if the value contains spaces
        if ($argumentValue.Contains(" "))
        {
            $argumentValue = "'" + $argumentValue + "'"
        }
        write-verbose "Argument value after transformation : $argumentValue"

        #write the result
        Write-Output $argumentValue

    }

}





